﻿using System;
using System.Collections.Generic;
using PnJ.Aliyun.OTS.Models;
using System.Net;
using System.IO;
using PnJ.Aliyun.OTS.Util;

namespace PnJ.Aliyun.OTS
{
    public partial class OTSClient
    {
        #region 属性
        /// <summary>
        /// 实例名称
        /// </summary>
        public string InstanceName { get; private set; }
        /// <summary>
        /// 云服务节点
        /// </summary>
        public string Region { get; private set; }
        /// <summary>
        /// AccessKeyID
        /// </summary>
        public string AccessKeyID { get; private set; }
        /// <summary>
        /// AccessKeySecret
        /// </summary>
        public string AccessKeySecret { get; private set; }

        public OTSLinkType LinkType { get; private set; }
        private string RequestUrlTemplate
        {
            get
            {
                if (LinkType == OTSLinkType.Public)
                    return "http://{0}.{1}.ots.aliyuncs.com/{2}";
                else
                    return "http://{0}.{!}.ots-internal.aliyuncs.com/{2}";
            }
        }

        #endregion

        #region 请求封装
        /// <summary>
        /// 发送POST请求(同步)
        /// </summary>
        /// <param name="operation">操作名</param>
        /// <param name="bodyContent">数据内容</param>
        /// <returns></returns>
        public byte[] POST(string operation, byte[] bodyContent)
        {
            byte[] data = null;

            try
            {
                var request = GenerateOTSWebReqeust(operation, "POST", bodyContent);

                var response = request.GetResponse() as HttpWebResponse;

                var ns = response.GetResponseStream();
                MemoryStream ms = new MemoryStream();
                int actual;
                byte[] buffer = new byte[256];
                while ((actual = ns.Read(buffer, 0, 256)) > 0)
                {
                    ms.Write(buffer, 0, actual);
                }

                data = ms.ToArray();

                CheckResponse(operation, response.Headers, data);

            }
            catch
            {
                throw;
            }

            return data;
        }



        /// <summary>
        /// 发送POST请求（同步)
        /// </summary>
        /// <typeparam name="TRequest"></typeparam>
        /// <typeparam name="TResponse"></typeparam>
        /// <param name="operation"></param>
        /// <param name="requestMessage"></param>
        /// <returns></returns>
        public TResponse POST<TRequest, TResponse>(string operation, TRequest requestMessage)
            where TRequest : class
            where TResponse : class
        {
            byte[] requestBody;
            using (MemoryStream ms = new MemoryStream())
            {
                ProtoBuf.Serializer.Serialize<TRequest>(ms, requestMessage);
                requestBody = new byte[ms.Length];
                ms.Seek(0, SeekOrigin.Begin);
                ms.Read(requestBody, 0, requestBody.Length);
            }

            byte[] responseBody = POST(operation, requestBody);
            TResponse responseMessage = null;

            using (MemoryStream ms = new MemoryStream())
            {
                ms.Write(responseBody, 0, responseBody.Length);
                ms.Seek(0, SeekOrigin.Begin);
                responseMessage = ProtoBuf.Serializer.Deserialize<TResponse>(ms);
            }

            return responseMessage;
        }
        /// <summary>
        /// 发送POST请求（同步)
        /// </summary>
        /// <typeparam name="TRequest"></typeparam>
        /// <param name="operation"></param>
        /// <param name="requestMessage"></param>
        /// <returns></returns>
        public bool POST<TRequest>(string operation, TRequest requestMessage)
            where TRequest : class
        {
            try
            {
                byte[] requestBody;
                using (MemoryStream ms = new MemoryStream())
                {
                    ProtoBuf.Serializer.Serialize<TRequest>(ms, requestMessage);
                    requestBody = new byte[ms.Length];
                    ms.Seek(0, SeekOrigin.Begin);
                    ms.Read(requestBody, 0, requestBody.Length);
                }

                POST(operation, requestBody);
                return true;
            }
            catch
            {
                return false;
            }


        }
        #endregion

        public OTSClient(string accessKeyId, string accessKeySecret, string instanceName, string region, OTSLinkType linkType = OTSLinkType.Public)
        {
            if (string.IsNullOrEmpty(accessKeyId))
                throw new ArgumentNullException("accessKeyId", "accessKeyId不能为空");
            if (string.IsNullOrEmpty(accessKeySecret))
                throw new ArgumentNullException("accessKeySecret", "accessKeySecret不能为空");
            if (string.IsNullOrEmpty(instanceName))
                throw new ArgumentNullException("instanceName", "instanceName不能为空");
            if (string.IsNullOrEmpty(region))
                throw new ArgumentNullException("region", "region不能为空");

            this.AccessKeyID = accessKeyId;
            this.AccessKeySecret = accessKeySecret;
            this.InstanceName = instanceName;
            this.Region = region;
            this.LinkType = linkType;
        }
        public PutRowResponse PutRowRequest(String tableName, List<Column> pks, List<Column> columns)
        {
            //RowBiz rowbiz = new RowBiz();
            //var link = new AliOSTLink("xjkCxNfBUKKB5lXb", "uySG71SoH1cCSOLgDuMPZe52cFKBC8", "pnj", "cn-shenzhen");
            //MemoryStream ms = new MemoryStream();
            //ProtoBuf.Serializer.Serialize<PutRowRequest>(ms, rowbiz.PutRowRequest(tableName, pks, columns));
            //byte[] bytes = new byte[ms.Length];
            //ms.Seek(0, SeekOrigin.Begin);
            //ms.Read(bytes, 0, bytes.Length);
            //var message = POST("PutRow", bytes);
            //return rowbiz.PutRowResponse(message);

            return null;
        }


        #region 帮助方法
        /// <summary>
        /// 生成WebReqeust
        /// </summary>
        /// <param name="operation">操作名</param>
        /// <param name="method">方法(GET/POST)</param>
        /// <param name="bodyContent">数据内容</param>
        /// <returns></returns>
        private HttpWebRequest GenerateOTSWebReqeust(string operation, string method, byte[] bodyContent)
        {
            if (string.IsNullOrEmpty(operation))
                throw new ArgumentNullException("operation");
            if (string.IsNullOrEmpty(method))
                throw new ArgumentNullException("method");
            if (bodyContent == null)
                throw new ArgumentNullException("bodyContent");
            if (bodyContent.Length > 2097152)
                throw new Exception("内容长度不能超过2MB");


            string requestUrl = string.Format(RequestUrlTemplate, InstanceName, Region, operation);

            HttpWebRequest request = WebRequest.Create(requestUrl) as HttpWebRequest;

            request.Method = method;

            request.Timeout = 20000;

            var x_ots_date = DateTime.Now.ToUniversalTime().ToString("r");
            request.Headers.Add("x-ots-date", x_ots_date);

            var x_ots_apiversion = "2014-08-08";
            request.Headers.Add("x-ots-apiversion", x_ots_apiversion);

            var x_ots_accesskeyid = AccessKeyID;
            request.Headers.Add("x-ots-accesskeyid", x_ots_accesskeyid);

            var x_ots_instancename = InstanceName;
            request.Headers.Add("x-ots-instancename", x_ots_instancename);

            var x_ots_contentmd5 = EncryptHelper.ContentMd5(bodyContent);
            request.Headers.Add("x-ots-contentmd5", x_ots_contentmd5);


            var x_ots_signature = AliyunHelper.GetSignature(AccessKeySecret, "/" + operation, method, request.Headers);
            request.Headers.Add("x-ots-signature", x_ots_signature);

            var ns = request.GetRequestStream();
            ns.Write(bodyContent, 0, bodyContent.Length);

            return request;
        }

        private void CheckResponse(string operation, WebHeaderCollection headers, byte[] bodyContent)
        {
            var authorization = headers["Authorization"];
            var x_ots_contentmd5 = headers["x-ots-contentmd5"];
            var x_ots_date = headers["x-ots-date"];

            var x_ots_authorization = AliyunHelper.GetAuthorization(AccessKeyID, AccessKeySecret, "/" + operation, headers);

            if (authorization != x_ots_authorization)
                throw new Exception("响应头中包含的签名不正确");

            var contentmd5 = EncryptHelper.ContentMd5(bodyContent);
            if (x_ots_contentmd5 != contentmd5)
                throw new Exception("x-ots-contentmd5校验失败");

            var otsDate = DateTime.Parse(x_ots_date);
            var dateSpan = DateTime.Now - otsDate;
            if (Math.Abs(dateSpan.TotalMinutes) >= 15)
                throw new Exception("时间与客户端时间相差15分钟");
        }
        #endregion
    }
}
